package com.snake.springboot.custom.service.impl.points;

import com.github.pagehelper.Page;
import com.snake.springboot.custom.api.model.base.CallbackModel;
import com.snake.springboot.custom.api.model.common.CommonVal;
import com.snake.springboot.custom.api.model.pay.PayActModel;
import com.snake.springboot.custom.api.model.pay.PayActRetModel;
import com.snake.springboot.custom.api.model.pay.PayMsgModel;
import com.snake.springboot.custom.api.model.points.PointsChangeModel;
import com.snake.springboot.custom.api.model.points.PointsChangeType;
import com.snake.springboot.custom.api.model.points.PointsRgeRecordModel;
import com.snake.springboot.custom.api.model.points.PointsRgeTempModel;
import com.snake.springboot.custom.api.request.bill.BillPayedAddModel;
import com.snake.springboot.custom.api.request.bill.BillRetEdModel;
import com.snake.springboot.custom.api.request.page.PageQuery;
import com.snake.springboot.custom.api.request.points.PointsRechargeReq;
import com.snake.springboot.custom.api.request.points.PointsRetReq;
import com.snake.springboot.custom.api.request.points.PointsRgeRecordQuery;
import com.snake.springboot.custom.api.response.points.PointsRechargeRsp;
import com.snake.springboot.custom.api.service.base.CustPointsService;
import com.snake.springboot.custom.api.service.points.PointsRgeRecordService;
import com.snake.springboot.custom.api.service.points.PointsRgeTempService;
import com.snake.springboot.custom.dao.mybatis.entity.points.PointsRetRecordDO;
import com.snake.springboot.custom.dao.mybatis.entity.points.PointsRgeRecordDO;
import com.snake.springboot.custom.dao.mybatis.mappers.points.PointsRetRecordMapper;
import com.snake.springboot.custom.dao.mybatis.mappers.points.PointsRgeRecordMapper;
import com.snake.springboot.custom.service.assemble.points.PointsRetRecordAssembler;
import com.snake.springboot.custom.service.assemble.points.PointsRgeRecordAssemble;
import com.snake.springboot.custom.service.port.adapter.event.BillPayedAddEvent;
import com.snake.springboot.custom.service.port.adapter.event.BillRetEdEvent;
import com.snake.springboot.custom.service.port.adapter.event.PayPlatEvent;
import com.snake.springboot.custom.service.port.adapter.event.PayPlatRetEvent;
import com.snake.springboot.custom.service.utils.PageUtils;
import org.snake.common.code.SerialNumberGeneratorFactory;
import org.snake.common.exception.OperationException;
import org.snake.common.result.ReturnList;
import org.snake.common.utils.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

@Service
public class PointsRgeRecordServiceImpl implements PointsRgeRecordService{

    @Autowired
    private PointsRgeRecordMapper pointsRgeRecordMapper;
    @Autowired
    private PointsRgeTempService tempService;
    @Autowired
    private CustPointsService pointsService;
    @Autowired
    private PointsRetRecordMapper retRecordMapper;
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public PointsRgeRecordModel addPointsRgeRecord(PointsRgeRecordModel model) {
        PointsRgeRecordDO recordDO = PointsRgeRecordAssemble.recordDOAss(model,true);
        pointsRgeRecordMapper.insert(recordDO);
        return model;
    }

    /**
     * 算力充值
     * 写记录
     * 回调支付网关
     * @param model
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PointsRechargeRsp recharge(PointsRechargeReq model) {
        PointsRechargeRsp rsp = new PointsRechargeRsp();
        //查询模板信息是否符合支付信息
        PointsRgeTempModel tempModel = tempService.queryById(model.getTempId());
        checkLimitBuy(model,tempModel);
        PointsRgeRecordDO recordDO = PointsRgeRecordAssemble.recordDOAss(model,tempModel);
        pointsRgeRecordMapper.insert(recordDO);
        rsp.setRecordId(recordDO.getRecordId());
        //如果免支付直接写记录
        if (CommonVal.Y.equals(recordDO.getIsPay())){
            PointsChangeModel changeModel = PointsRgeRecordAssemble.changeModelAss(recordDO, PointsChangeType.RECHARGE,"算力充值");
            pointsService.add(changeModel);
            rsp.setIsPay(CommonVal.Y);
            //将数据写入账单
            BillPayedAddModel addModel = PointsRgeRecordAssemble.toBillPayedModel(recordDO);
            BillPayedAddEvent event = new BillPayedAddEvent(this,addModel);
            applicationContext.publishEvent(event);
            return rsp;
        }

        //调起支付模型
        PayActModel actModel = PointsRgeRecordAssemble.actModelAss(model,recordDO);
        actModel.setCallbackUrl("http://snake-custom/custom/points/rge-record/callback");
        PayPlatEvent event = new PayPlatEvent(this, actModel);
        applicationContext.publishEvent(event);
        PayMsgModel result = event.getResult();
        pointsRgeRecordMapper.addPayId(recordDO.getRecordId(),result.getPayId());
        rsp.setPayMsgModel(result);
        rsp.setIsPay(CommonVal.N);
        return rsp;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(CallbackModel model) {
        PointsRgeRecordDO recordDO = pointsRgeRecordMapper.queryById(model.getPayCode());
        if (CommonVal.Y.equals(recordDO.getIsPay())){
            return;
        }
        PointsChangeModel changeModel = PointsRgeRecordAssemble.changeModelAss(recordDO, PointsChangeType.RECHARGE,"算力充值");
        pointsService.add(changeModel);
        recordDO.setPayAccType(model.getPayAccType());
        recordDO.setIsPay(CommonVal.Y);
        recordDO.setBillId(SerialNumberGeneratorFactory.buildId4Bean());
        pointsRgeRecordMapper.updatePayState(recordDO);
        //将数据写入账单
        BillPayedAddModel addModel = PointsRgeRecordAssemble.toBillPayedModel(recordDO);
        BillPayedAddEvent event = new BillPayedAddEvent(this,addModel);
        applicationContext.publishEvent(event);

    }

    /**
     * 算力退款
     * 1.查询是否支付且没有退过款
     * 2.更改支付状态
     * 3.扣减算力
     * 4.写退款记录
     * 5.回调支付网关退款
     * @param model
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pointsRet(PointsRetReq model) {
        PointsRgeRecordDO recordDO = pointsRgeRecordMapper.queryById(model.getRecordId());
        if (CommonVal.Y.equals(recordDO.getIsPay()) && !CommonVal.N.equals(recordDO.getRetState())){
            return;
        }
        recordDO.setRetState(CommonVal.Y);
        recordDO.setRetAmount(recordDO.getPayAmount());
        pointsRgeRecordMapper.updateRet(recordDO);
        PointsChangeModel changeModel = PointsRgeRecordAssemble.changeModelAss(recordDO, PointsChangeType.RET,"算力退款");
        pointsService.reduce(changeModel);
        PointsRetRecordDO retRecordDO = PointsRetRecordAssembler.retDOAss(recordDO);
        retRecordDO.setRemark(model.getRetRemark());
        retRecordMapper.insert(retRecordDO);
        if (BigDecimal.ZERO.compareTo(recordDO.getPayAmount()) == 0){
            return;
        }
        //退款账单信息
        BillRetEdModel retEdModel = PointsRgeRecordAssemble.toBillRedEdModel(recordDO);
        BillRetEdEvent billRetEdEvent = new BillRetEdEvent(this, retEdModel);
        applicationContext.publishEvent(billRetEdEvent);
        //调起支付模型
        PayActRetModel actModel = PointsRgeRecordAssemble.actRetModelAss(recordDO,model);
        PayPlatRetEvent retEvent = new PayPlatRetEvent(this, actModel);
        applicationContext.publishEvent(retEvent);
    }

    @Override
    public void modifyPointsRgeRecord(PointsRgeRecordModel model) {
        PointsRgeRecordDO recordDO = PointsRgeRecordAssemble.recordDOAss(model,false);
        pointsRgeRecordMapper.update(recordDO);
    }

    @Override
    public void removePointsRgeRecord(String id) {
        pointsRgeRecordMapper.delete(id);
    }

    @Override
    public ReturnList<PointsRgeRecordModel> queryPointsRgeRecordList(PageQuery<PointsRgeRecordQuery> params) {
        Page<Object> objects = PageUtils.forcePage(params.getPage());
        PointsRgeRecordDO queryDO = PointsRgeRecordAssemble.recordDOAss(params.getParams());
        List<PointsRgeRecordDO> list = pointsRgeRecordMapper.queryList(queryDO);
        List<PointsRgeRecordModel> modelList = ListUtils.copy(list, PointsRgeRecordModel.class);
        return new ReturnList<>(modelList,objects.getTotal());
    }

    /**
     * 校验限购数量
     * @param model
     * @param tempModel
     */
    public void checkLimitBuy(PointsRechargeReq model,PointsRgeTempModel tempModel){
        if (CommonVal.Y.equals(tempModel.getLimitBuy())){
            int limitBuy =pointsRgeRecordMapper.queryLimitBuy(model.getCustomId(),tempModel.getTempId());
            OperationException.checkIsTrue(limitBuy+1<=tempModel.getLimitBuy(),"超过购买次数限制");
        }
    }
}
